/*
 * Copyright 2009 Colin Prinn
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package net.recursv.motific.utils.files;

import net.recursv.motific.utils.StringList;

import javax.microedition.io.Connection;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import javax.microedition.io.file.FileSystemRegistry;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Hashtable;

/**
 * User: recursv
 * Date: 10/08/2009
 * Time: 9:17:20 AM
 */
public final class FileSystem {
    private static final char EQUALS = '=';
    private static final char NEW_LINE = '\n';
    private static final String URL_PREFIX = "file:///";
    private static final char PATH_SEPERATOR = '/';
    private static final String PATH_SEPERATOR_STRING = String.valueOf(PATH_SEPERATOR);
    private static final char HASH = '#';

    private FileSystem() {
    }

    public static StringList listRoots() {
        StringList list = new StringList();
        Enumeration enumeration = FileSystemRegistry.listRoots();
        while (enumeration.hasMoreElements())
            list.add((String) enumeration.nextElement());
        return list;
    }

    public static InputStream read(String filename) throws IOException {
        InputStream is = NullInputStream.getInstance();
        FileConnection connection = null;
        try {
            connection = (FileConnection) Connector.open(filename);
            if (connection.exists() && connection.canRead()) {
                is = connection.openInputStream();
            }
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
        return is;
    }

    public static boolean write(String filename, InputStream is) throws IOException {
        OutputStream os = NullOutputStream.getInstance();
        FileConnection connection = null;
        try {
            connection = (FileConnection) Connector.open(getUrl(filename));
            if (connection.isDirectory())
                return false;
            if (!connection.exists())
                connection.create();
            os = connection.openOutputStream();
            byte[] bytes = new byte[1024];
            int i = is.read(bytes);
            while (i != -1) {
                os.write(bytes, 0, i);
                i = is.read(bytes);
            }
        } finally {
            safeClose(is);
            safeClose(os);
            safeClose(connection);
        }
        return true;
    }

    public static void safeClose(InputStream stream) {
        try {
            stream.close();
        } catch (Throwable ignored) {
        }
    }

    public static void safeClose(OutputStream stream) {
        try {
            stream.flush();
            stream.close();
        } catch (Throwable ignored) {
        }
    }

    public static void safeClose(Connection con) {
        try {
            con.close();
        } catch (Throwable ignored) {
        }
    }

    public static Hashtable readPropertiesFile(String filename) throws IOException {
        return readPropertiesTable(read(filename));
    }

    public static Hashtable readPropertiesTable(InputStream is) throws IOException {
        Hashtable hashtable = new Hashtable();
        if (is != null) {
            StringBuffer buffer = new StringBuffer();

            try {
                char c;
                boolean inComment = false;
                String name = "";
                while ((c = (char) is.read()) != (char) -1) {
                    if (c == EQUALS) {
                        name = buffer.toString();
                        buffer.delete(0, buffer.length());
                    } else if (c == NEW_LINE) {
                        if (buffer.length() > 0)
                            hashtable.put(name, buffer.toString());
                        buffer.delete(0, buffer.length());
                        inComment = false;
                    } else if (c == HASH) {
                        inComment = true;
                    } else if (!inComment)
                        buffer.append(c);
                }
            } finally {
                safeClose(is);
            }
        }
        return hashtable;
    }

    public static String readString(String filename) throws IOException {
        InputStream is = read(filename);
        StringBuffer buffer = new StringBuffer();
        byte[] bytes = new byte[1024];

        for (int count = is.read(bytes); count != -1; count = is.read(bytes))
            buffer.append(new String(bytes, 0, count));

        safeClose(is);
        return buffer.toString();
    }

    public static boolean write(String filename, String value) throws IOException {
        return write(filename, new StringInputStream(value));
    }

    public static boolean writePropertiesFile(String filename, Hashtable properties) throws IOException {
        StringBuffer buffer = new StringBuffer();

        Enumeration enumeration = properties.keys();
        while (enumeration.hasMoreElements()) {
            String key = (String) enumeration.nextElement();
            buffer.append(key);
            buffer.append(EQUALS);
            buffer.append(properties.get(key));
            buffer.append(NEW_LINE);
        }

        return write(filename, buffer.toString());
    }

    public static StringList listFiles(String root) throws IOException {
        StringList list = new StringList();
        listFiles(getUrl(root), list);
        return list;
    }

    private static void listFiles(String url, StringList stringList) throws IOException {
        FileConnection connection = null;
        try {
            connection = (FileConnection) Connector.open(url);
            if (connection.exists()) {
                if (connection.isDirectory()) {
                    Enumeration enumeration = connection.list();
                    while (enumeration.hasMoreElements())
                        listFiles(connection.getURL().concat((String) enumeration.nextElement()), stringList);
                } else
                    stringList.add(connection.getURL());
            }
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }

    public static String getUrl(String s) {
        if (s.startsWith(URL_PREFIX))
            return (s.substring(URL_PREFIX.length()).indexOf(PATH_SEPERATOR) == -1 ? s.concat(PATH_SEPERATOR_STRING) : s);
        else if (s.startsWith(PATH_SEPERATOR_STRING))
            return "file://".concat(s);
        else
            return URL_PREFIX.concat(s.indexOf(PATH_SEPERATOR) == -1 ? s.concat(PATH_SEPERATOR_STRING) : s);

    }

    public static boolean delete(String s) throws IOException {
        FileConnection connection = null;
        try {
            connection = (FileConnection) Connector.open(getUrl(s));
            if (connection.exists()) {
                connection.delete();
                return true;
            } else
                return false;
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
    }

    public static boolean fileExists(String s) throws IOException {
        boolean retval;

        FileConnection connection = null;
        try {
            connection = (FileConnection) Connector.open(getUrl(s));
            retval = connection.exists();
        } finally {
            if (connection != null) {
                connection.close();
            }
        }

        return retval;
    }

    private static InputStream openResourceStream(String name) {
        return name.getClass().getResourceAsStream(name);
    }

    public static Hashtable readPropertiesResource(String name) throws IOException {
        return readPropertiesTable(openResourceStream(name));
    }
}
